package com.syf.kit.core.dao.impl;

import com.syf.kit.core.dao.BaseDTO;
import com.syf.kit.core.dao.SyfRdsDaoUtil;
import com.syf.kit.core.dao.mapper.MapperType;
import com.syf.kit.core.dao.mapper.SyfMapper;
import com.syf.kit.core.dao.mapper.registry.SyfAbstractDtoMapperRegistry;
import com.syf.kit.core.dao.sql.JdbcSQLProcessor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;


/**
 * 只有一个datasource的普通dao manager
 */
@Slf4j
public class SyfSimpleRdsDaoUtil implements SyfRdsDaoUtil {
    private SqlSession sqlSession;

    /**
     * 自动提交标志，手工开启事务后，此值失效
     */
    private boolean autoCommit;

    /**
     * SQL执行器，适用于复杂sql和多表查询等场景.
     */
    private JdbcSQLProcessor jdbcSQLProcessor;

    /**
     * 事务开启状态
     */
    private boolean IS_OPEN_TX = false;

    private SqlSessionFactory sqlSessionFactory;

    /**
     * mapper管理器，里面存储了通过dto class映射出来的mapper对象，适用于单表操作.
     */
    private SyfAbstractDtoMapperRegistry mapperRegistry;

    /**
     * 是否已经关闭
     */
    private boolean isClosed = true;

    public SyfSimpleRdsDaoUtil(SqlSessionFactory sqlSessionFactory, SyfAbstractDtoMapperRegistry mapperRegistry, boolean autoCommit, MapperType mapperType) {
        this.sqlSessionFactory = sqlSessionFactory;
        this.autoCommit = autoCommit;
        this.mapperRegistry = mapperRegistry;
    }

    @Override
    public JdbcSQLProcessor getSQLProcessor() {
        openConnection();
        if (jdbcSQLProcessor == null) {
            this.jdbcSQLProcessor = new JdbcSQLProcessor(sqlSession);
        }

        return jdbcSQLProcessor;
    }

    @Override
    public <T extends BaseDTO> SyfMapper<T> getMapper(Class<T> dtoClass) {
        openConnection();
        return (SyfMapper<T>) sqlSession.getMapper(mapperRegistry.getMapperType(dtoClass));
    }


    @Override
    public void openConnection() {
        if (sqlSession == null) {
            this.sqlSession = sqlSessionFactory.openSession(autoCommit);
            this.isClosed = false;
        }
    }

    @Override
    public void close() {
        if (!isClosed) {
            sqlSession.close();
            isClosed = true;
        }
    }

    @Override
    public void beginTransaction() {
        // 如果是默认自动提交，则开启事务后，就不能默认提交事务
        if (autoCommit) {
            // 先关闭原有会自动提交的sqlSession
            sqlSession.close();
            // 再开启一个新的不自动提交的sqlSession
            sqlSession = sqlSessionFactory.openSession(false);
        }

        IS_OPEN_TX = true;
    }

    @Override
    public void closeTransaction() {
        if (!IS_OPEN_TX) {
            log.warn("未曾开启db事务，但却显式关闭db事务！请注意代码编写规范");
            return;
        }

        // 为了反正有事务未提交，而连接却关闭的情况，会进行主动回滚
        if (getSQLProcessor().isDirt()) {
            sqlSession.rollback(true);
            getSQLProcessor().resetDirt();
        } else {
            sqlSession.rollback();
        }

        // 如果是默认自动提交，则关闭事务后，则需要还原为会自动提交的sqlSession
        if (autoCommit) {
            // 先关闭现有的不自动提交的sqlSession
            sqlSession.close();
            // 再开启一个新的自动提交的sqlSession
            sqlSession = sqlSessionFactory.openSession(true);
        }

        IS_OPEN_TX = false;
    }

    @Override
    public void commitTransaction() {
        if (!IS_OPEN_TX) {
            log.warn("未曾开启db事务，但却显式提交db事务！请注意代码编写规范");
            return;
        }

        if (getSQLProcessor().isDirt()) {
            sqlSession.commit(true);
            getSQLProcessor().resetDirt();
        } else {
            sqlSession.commit();
        }
    }

    @Override
    public void rollbackTransaction() {
        if (!IS_OPEN_TX) {
            log.warn("未曾开启db事务，但却显式回滚db事务！请注意代码编写规范");
            return;
        }

        if (getSQLProcessor().isDirt()) {
            sqlSession.commit(true);
            getSQLProcessor().resetDirt();
        } else {
            sqlSession.commit();
        }
    }

    /**
     * 是否为自动提交
     */
    public boolean isAutoCommit() {
        return autoCommit;
    }

    /**
     * 是否已开启了事务
     */
    public boolean isOpenTx() {
        return IS_OPEN_TX;
    }

    @Override
    public void releaseResource() {
        close();
    }
}
